/* Copyright (c) Microsoft Corporation.
   Licensed under the MIT License. */

/* Copyright (c) Microsoft Corporation.
   Licensed under the MIT License. */

/***************************************************************************
	Author: ShonK
	Project: Kauai
	Reviewed:
	Copyright (c) Microsoft Corporation

	Chunky file classes. See comments in chunk.cpp.

***************************************************************************/
#ifndef CHUNK_H
#define CHUNK_H


/***************************************************************************
	These must be unsigned longs!  We sort on them and assume in the code
	that they are unsinged.
***************************************************************************/
typedef ulong CTG;	// chunk tag/type
typedef ulong CNO;	// chunk number
typedef ulong CHID;	// child chunk id

enum
	{
	fcflNil = 0x0000,
	fcflWriteEnable = 0x0001,
	fcflTemp = 0x0002,
	fcflMark = 0x0004,
	fcflAddToExtra = 0x0008,

	// This flag indicates that when data is read, it should first be
	// copied to the extra file (if it's not already there). This is
	// for chunky files that are on a CD for which we want to cache data
	// to the hard drive.
	fcflReadFromExtra = 0x0010,

#ifdef DEBUG
	// for AssertValid
	fcflGraph = 0x4000, // check the graph structure for cycles
	fcflFull = fobjAssertFull,
#endif //DEBUG
	};


// chunk identification
struct CKI
	{
	CTG ctg;
	CNO cno;
	};
const BOM kbomCki = 0xF0000000;


// child chunk identification
struct KID
	{
	CKI cki;
	CHID chid;
	};
const BOM kbomKid = 0xFC000000;


/***************************************************************************
	Chunky file class.
***************************************************************************/
typedef class CFL *PCFL;
#define CFL_PAR BLL
#define kclsCFL 'CFL'
class CFL : public CFL_PAR
	{
	RTCLASS_DEC
	BLL_DEC(CFL, PcflNext)
	ASSERT
	MARKMEM

private:
	// chunk storage
	struct CSTO
		{
		PFIL pfil;	// the file
		FP fpMac;	// logical end of file (for writing new chunks)
		PGL pglfsm;	// free space map
		};

	PGG _pggcrp;		//the index
	CSTO _csto;			//the main file
	CSTO _cstoExtra;	//the scratch file

	bool _fAddToExtra: 1;
	bool _fMark: 1;
	bool _fFreeMapNotRead: 1;
	bool _fReadFromExtra: 1;
	bool _fInvalidMainFile: 1;

	// for deferred reading of the free map
	FP _fpFreeMap;
	long _cbFreeMap;

#ifndef CHUNK_BIG_INDEX
	struct RTIE
		{
		CTG ctg;
		CNO cno;
		long rti;
		};

	PGL _pglrtie;

	bool _FFindRtie(CTG ctg, CNO cno, RTIE *prtie = pvNil,
		long *pirtie = pvNil);
#endif //!CHUNK_BIG_INDEX

	// static member variables
	static long _rtiLast;
	static PCFL _pcflFirst;

private:
	// private methods
	CFL(void);
	~CFL(void);

	static ulong CFL::_GrffilFromGrfcfl(ulong grfcfl);

	bool _FReadIndex(void);
	bool _TValidIndex(void);
	bool _FWriteIndex(CTG ctgCreator);
	bool _FCreateExtra(void);
	bool _FAllocFlo(long cb, PFLO pflo, bool fForceOnExtra = fFalse);
	bool _FFindCtgCno(CTG ctg, CNO cno, long *picrp);
	void _GetUniqueCno(CTG ctg, long *picrp, CNO *pcno);
	void _FreeFpCb(bool fOnExtra, FP fp, long cb);
	bool _FAdd(long cb, CTG ctg, CNO cno, long icrp, PBLCK pblck);
	bool _FPut(long cb, CTG ctg, CNO cno, PBLCK pblck, PBLCK pblckSrc,
		void *pv);
	bool _FCopy(CTG ctgSrc, CNO cnoSrc, PCFL pcflDst, CNO *pcnoDst,
		bool fClone);
	bool _FFindMatch(CTG ctgSrc, CNO cnoSrc, PCFL pcflDst, CNO *pcnoDst);
	bool _FFindCtgRti(CTG ctg, long rti, CNO cnoMin, CNO *pcnoDst);
	bool _FDecRefCount(long icrp);
	void _DeleteCore(long icrp);
	bool _FFindChild(long icrpPar, CTG ctgChild, CNO cnoChild, CHID chid,
		long *pikid);
	bool _FAdoptChild(long icrpPar, long ikid, CTG ctgChild, CNO cnoChild,
		CHID chid, bool fClearLoner);
	void _ReadFreeMap(void);
	bool _FFindChidCtg(CTG ctgPar, CNO cnoPar, CHID chid, CTG ctg, KID *pkid);
	bool _FSetName(long icrp, PSTN pstn);
	bool _FGetName(long icrp, PSTN pstn);
	void _GetFlo(long icrp, PFLO pflo);
	void _GetBlck(long icrp, PBLCK pblck);
	bool _FEnsureOnExtra(long icrp, FLO *pflo = pvNil);

	long _Rti(CTG ctg, CNO cno);
	bool _FSetRti(CTG ctg, CNO cno, long rti);

public:
	// static methods
	static PCFL PcflFirst(void)
		{ return _pcflFirst; }
	static PCFL PcflOpen(FNI *pfni, ulong grfcfl);
	static PCFL PcflCreate(FNI *pfni, ulong grfcfl);
	static PCFL PcflCreateTemp(FNI *pfni = pvNil);
	static PCFL PcflFromFni(FNI *pfni);

	static void ClearMarks(void);
	static void CloseUnmarked(void);
	#ifdef CHUNK_STATS
		static void DumpStn(PSTN pstn, PFIL pfil = pvNil);
	#endif //CHUNK_STATS

	virtual void Release(void);
	bool FSetGrfcfl(ulong grfcfl, ulong grfcflMask = (ulong)~0);
	void Mark(void) { _fMark = fTrue; }
	void SetTemp(bool f) { _csto.pfil->SetTemp(f); }
	bool FTemp(void) { return _csto.pfil->FTemp(); }
	void GetFni(FNI *pfni) { _csto.pfil->GetFni(pfni); }
	bool FSetFni(FNI *pfni) { return _csto.pfil->FSetFni(pfni); }
	long ElError(void);
	void ResetEl(long el = elNil);
	bool FReopen(void);

	// finding and reading chunks
	bool FOnExtra(CTG ctg, CNO cno);
	bool FEnsureOnExtra(CTG ctg, CNO cno);
	bool FFind(CTG ctg, CNO cno, BLCK *pblck = pvNil);
	bool FFindFlo(CTG ctg, CNO cno, PFLO pflo);
	bool FReadHq(CTG ctg, CNO cno, HQ *phq);
	void SetPacked(CTG ctg, CNO cno, bool fPacked);
	bool FPacked(CTG ctg, CNO cno);
	bool FUnpackData(CTG ctg, CNO cno);
	bool FPackData(CTG ctg, CNO cno);

	// creating and replacing chunks
	bool FAdd(long cb, CTG ctg, CNO *pcno, PBLCK pblck = pvNil);
	bool FAddPv(void *pv, long cb, CTG ctg, CNO *pcno);
	bool FAddHq(HQ hq, CTG ctg, CNO *pcno);
	bool FAddBlck(PBLCK pblckSrc, CTG ctg, CNO *pcno);
	bool FPut(long cb, CTG ctg, CNO cno, PBLCK pblck = pvNil);
	bool FPutPv(void *pv, long cb, CTG ctg, CNO cno);
	bool FPutHq(HQ hq, CTG ctg, CNO cno);
	bool FPutBlck(PBLCK pblck, CTG ctg, CNO cno);
	bool FCopy(CTG ctgSrc, CNO cnoSrc, PCFL pcflDst, CNO *pcnoDst);
	bool FClone(CTG ctgSrc, CNO cnoSrc, PCFL pcflDst, CNO *pcnoDst);
	void SwapData(CTG ctg1, CNO cno1, CTG ctg2, CNO cno2);
	void SwapChildren(CTG ctg1, CNO cno1, CTG ctg2, CNO cno2);
	void Move(CTG ctg, CNO cno, CTG ctgNew, CNO cnoNew);

	//creating child chunks
	bool FAddChild(CTG ctgPar, CNO cnoPar, CHID chid,
		long cb, CTG ctg, CNO *pcno, PBLCK pblck = pvNil);
	bool FAddChildPv(CTG ctgPar, CNO cnoPar, CHID chid,
		void *pv, long cb, CTG ctg, CNO *pcno);
	bool FAddChildHq(CTG ctgPar, CNO cnoPar, CHID chid,
		HQ hq, CTG ctg, CNO *pcno);

	// deleting chunks
	void Delete(CTG ctg, CNO cno);
	void SetLoner(CTG ctg, CNO cno, bool fLoner);
	bool FLoner(CTG ctg, CNO cno);

	// chunk naming
	bool FSetName(CTG ctg, CNO cno, PSTN pstn);
	bool FGetName(CTG ctg, CNO cno, PSTN pstn);

	// graph structure
	bool FAdoptChild(CTG ctgPar, CNO cnoPar,
		CTG ctgChild, CNO cnoChild, CHID chid = 0, bool fClearLoner = fTrue);
	void DeleteChild(CTG ctgPar, CNO cnoPar,
		CTG ctgChild, CNO cnoChild, CHID chid = 0);
	long CckiRef(CTG ctg, CNO cno);
	bool TIsDescendent(CTG ctg, CNO cno, CTG ctgSub, CNO cnoSub);
	void ChangeChid(CTG ctgPar, CNO cnoPar, CTG ctgChild, CNO cnoChild,
		CHID chidOld, CHID chidNew);

	// enumerating chunks
	long Ccki(void);
	bool FGetCki(long icki, CKI *pcki, long *pckid = pvNil, PBLCK pblck = pvNil);
	bool FGetIcki(CTG ctg, CNO cno, long *picki);
	long CckiCtg(CTG ctg);
	bool FGetCkiCtg(CTG ctg, long icki, CKI *pcki, long *pckid = pvNil,
		PBLCK pblck = pvNil);

	// enumerating child chunks
	long Ckid(CTG ctgPar, CNO cnoPar);
	bool FGetKid(CTG ctgPar, CNO cnoPar, long ikid, KID *pkid);
	bool FGetKidChid(CTG ctgPar, CNO cnoPar, CHID chid, KID *pkid);
	bool FGetKidChidCtg(CTG ctgPar, CNO cnoPar, CHID chid, CTG ctg, KID *pkid);
	bool FGetIkid(CTG ctgPar, CNO cnoPar, CTG ctg, CNO cno, CHID chid, long *pikid);

	// Serialized chunk forests
	bool FWriteChunkTree(CTG ctg, CNO cno, PFIL pfilDst, FP fpDst, long *pcb);
	static PCFL PcflReadForestFromFlo(PFLO pflo, bool fCopyData);
	bool FForest(CTG ctg, CNO cno);
	void SetForest(CTG ctg, CNO cno, bool fForest = fTrue);
	PCFL PcflReadForest(CTG ctg, CNO cno, bool fCopyData);

	// writing
	bool FSave(CTG ctgCreator, FNI *pfni = pvNil);
	bool FSaveACopy(CTG ctgCreator, FNI *pfni);
	};


/***************************************************************************
	Chunk graph enumerator
***************************************************************************/
enum
	{
	// inputs
	fcgeNil = 0x0000,
	fcgeSkipToSib = 0x0001,

	// outputs
	fcgePre = 0x0010,
	fcgePost = 0x0020,
	fcgeRoot = 0x0040,
	fcgeError = 0x0080
	};

#define CGE_PAR BASE
#define kclsCGE 'CGE'
class CGE : public CGE_PAR
	{
	RTCLASS_DEC
	ASSERT
	MARKMEM
	NOCOPY(CGE)

private:
	//data enumeration push state
	struct DPS
		{
		KID kid;
		long ikid;
		};

	// enumeration states
	enum
		{
		esStart,	//waiting to start the enumeration
		esGo,		//go to the next node
		esGoNoSkip,	//there are no children to skip, so ignore fcgeSkipToSib
		esDone		//we're done with the enumeration
		};

	long _es;		//current state
	PCFL _pcfl;		//the chunky file
	PGL _pgldps;	//our stack of DPSs
	DPS _dps;		//the current DPS

public:
	CGE(void);
	~CGE(void);

	void Init(PCFL pcfl, CTG ctg, CNO cno);
	bool FNextKid(KID *pkid, CKI *pckiPar, ulong *pgrfcgeOut, ulong grfcgeIn);
	};


#ifdef CHUNK_STATS
extern bool vfDumpChunkRequests;
#endif //CHUNK_STATS

#endif //!CHUNK_H

